package com.bestom.waterquality.logic;

import android.app.Activity;
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;

import com.bestom.waterquality.R;
import com.bestom.waterquality.WaterQualityApplication;
import com.bestom.waterquality.entity.Constant;
import com.bestom.waterquality.entity.NodeBean;
import com.bestom.waterquality.services.MQTTService;
import com.bestom.waterquality.services.MQTTService2;
import com.bestom.waterquality.util.CommandExec;
import com.bestom.waterquality.util.ToastUtil;

import cn.wch.ch934xlib.CH934XManager;
import cn.wch.ch934xlib.exception.UartLibException;
import cn.wch.ch934xlib.gpio.GPIO_DIR;
import cn.wch.ch934xlib.gpio.GPIO_VALUE;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * IO设置
 */
public class IOSet {

    private Context mContext;
    private Activity mActivity;

    private IOSet() {
    }

    private static IOSet _instance = null;
    private UsbDevice mUsbDevice;

    public static IOSet getInstance() {
        if (_instance == null)
            _instance = new IOSet();
        return _instance;
    }

    private Spinner output1;
    private Spinner output2;
    private Spinner output3;
    private Spinner output4;

    /**用于判断是手动选择的还是程序自动控制选择的，防止重复执行两次修改后的逻辑*/
    private boolean flag;

    public void setDataAndListen(Activity activity, Context context, View view) {

        if (mContext == null) {
            mContext = context;
            mActivity = activity;
            mUsbDevice = SerialPortSet.getInstance().getDevice();
            output1 = view.findViewById(R.id.output_1);
            output2 = view.findViewById(R.id.output_2);
            output3 = view.findViewById(R.id.output_3);
            output4 = view.findViewById(R.id.output_4);

            output1.setSelection(1);
            output1.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if (flag) setGPIOValue(mUsbDevice, 0, i == 1 ? GPIO_VALUE.HIGH : GPIO_VALUE.LOW);
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {

                }
            });

            output2.setSelection(CommandExec.execCommand(Constant.CAT_GP15, false).successMsg.equals("1") ? 1 : 0);
            output2.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if (flag) CommandExec.execCommand(i == 0 ? Constant.OPEN_GP15 : Constant.CLOSE_GP15, false);
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {

                }
            });

            output3.setSelection(1);
            output3.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if (flag) setGPIOValue(mUsbDevice, 2, i == 1 ? GPIO_VALUE.HIGH : GPIO_VALUE.LOW);
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {

                }
            });

            output4.setSelection(CommandExec.execCommand(Constant.CAT_GP17, false).successMsg.equals("1") ? 1 : 0);
            output4.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if (flag) CommandExec.execCommand(i == 0 ? Constant.OPEN_GP17 : Constant.CLOSE_GP17, false);
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {

                }
            });

            if (mUsbDevice != null) setGPIODir(mUsbDevice, 0, GPIO_DIR.IN, null);
        }
    }

    private void setGPIODir(UsbDevice device, int gpioNumber, GPIO_DIR dir, GPIO_VALUE value) {
        Observable.create((ObservableOnSubscribe<String>) emitter -> {

            try {
                boolean ret = CH934XManager.getInstance().setGPIODir(device, gpioNumber, dir);
                if (!ret) {
                    emitter.onError(new Throwable("operation failed!"));
                } else {
                    emitter.onComplete();
                }
            } catch (UartLibException e) {
                emitter.onError(new Throwable(e.getMessage()));
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
                    }

                    @Override
                    public void onNext(@io.reactivex.annotations.NonNull String s) {

                    }

                    @Override
                    public void onError(@io.reactivex.annotations.NonNull Throwable e) {
                        ToastUtil.toast(e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        /*轮流进行设置值处理
                        * 先将CH934x芯片引出的 1，3 的 IO口设置为 IN，再读取该电平并显示
                        * 再将IO 口设置为 OUT，才可控制
                        * */
                        if (dir == GPIO_DIR.IN) {
                            if (gpioNumber == 0) setGPIODir(mUsbDevice, 2, GPIO_DIR.IN, null);
                            else if (gpioNumber == 2) getGPIOValue(mUsbDevice, 0);
                        } else if (dir == GPIO_DIR.OUT) setGPIOValue(mUsbDevice, gpioNumber, value);
                    }
                });
    }

    private void setGPIOValue(UsbDevice device, int gpioNumber, GPIO_VALUE value) {

        Observable.create((ObservableOnSubscribe<String>) emitter -> {

            try {
                boolean ret = CH934XManager.getInstance().setGPIOValue(device, gpioNumber, value);
                if (!ret) {
                    emitter.onError(new Throwable("operation failed!"));
                } else {
                    emitter.onComplete();
                }
            } catch (UartLibException e) {

                emitter.onError(new Throwable(e.getMessage()));
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
                    }

                    @Override
                    public void onNext(@io.reactivex.annotations.NonNull String s) {

                    }

                    @Override
                    public void onError(@io.reactivex.annotations.NonNull Throwable e) {
                        if (e.getMessage().contains("is not OUT"))
                            setGPIODir(device, gpioNumber, GPIO_DIR.OUT, value);
                        else {
                            showToast(e.getMessage());
                            error = e.getMessage();
                            result = "1";
                        }
                    }

                    @Override
                    public void onComplete() {
                        result = "0";
                        showToast("operation success!");
                    }
                });
    }

    private void getGPIOValue(UsbDevice device, int serialNumber) {

        Observable.create((ObservableOnSubscribe<String>) emitter -> {

            try {
                boolean ret = CH934XManager.getInstance().getGPIOValue(device);
                if (!ret) {
                    emitter.onError(new Throwable("get GPIO value fail!"));
                    return;
                }

                for (int i = serialNumber * 3; i < serialNumber * 3 + 3; i++) {
                    GPIO_VALUE gpio_value = CH934XManager.getInstance().queryGPIOValueFromCache(device, i);

                    if (i == serialNumber * 3) {
                        mActivity.runOnUiThread(() -> {
                            flag = false;
                            output1.setSelection(gpio_value == GPIO_VALUE.HIGH ? 1 : 0);
                        });

                    } else if (i == serialNumber * 3 + 2) {

                        mActivity.runOnUiThread(() -> {
                            flag = false;
                            output3.setSelection(gpio_value == GPIO_VALUE.HIGH ? 1 : 0);

                            emitter.onComplete();
                        });
                    }
                }

            } catch (UartLibException e) {
                emitter.onError(new Throwable(e.getMessage()));
            }
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
                    }

                    @Override
                    public void onNext(@io.reactivex.annotations.NonNull String s) {
                    }

                    @Override
                    public void onError(@io.reactivex.annotations.NonNull Throwable e) {
                        showToast(e.getMessage());
                        error = e.getMessage();
                    }

                    @Override
                    public void onComplete() {
                        flag = true;
                    }
                });
    }

    private final Handler handler = new Handler(Looper.getMainLooper());

    private void showToast(String message) {
        handler.post(() -> ToastUtil.create(mContext.getApplicationContext(), message).show());
    }

    private String result = "0";
    private String error = "1,3 Io口未找到对应芯片";
    public void setValue(String ioId, String setValue){
        if (ioId.isEmpty()) return;
        if (setValue.isEmpty()) return;
        StringBuilder status = new StringBuilder();
        int i = 0;
        String[] split = setValue.split(",");
        for (String id : ioId.split(",")){
            switch (id){
                case "1":
                    if (flag){
                        if (output1 != null) output1.setSelection(split[i].equals("0") ? 0 : 1);
                        else setGPIOValue(mUsbDevice, 0, split[i].equals("1") ? GPIO_VALUE.HIGH : GPIO_VALUE.LOW);
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if (i == split.length - 1) status.append(result);
                        else status.append(result).append(",");
                    }else {
                        if (i == split.length - 1) status.append("1");
                        else status.append("1,");
                    }

                    break;
                case "2":
                    if (output2 != null) output2.setSelection(split[i].equals("0") ? 0 : 1);
                    else CommandExec.execCommand(split[i].equals("1") ? Constant.OPEN_GP15 : Constant.CLOSE_GP15, false);
                    if (i == split.length - 1) status.append("0");
                    else status.append("0,");
                    break;
                case "3":
                    if (flag){
                        if (output3 != null) output3.setSelection(split[i].equals("0") ? 0 : 1);
                        else setGPIOValue(mUsbDevice, 0, split[i].equals("1") ? GPIO_VALUE.HIGH : GPIO_VALUE.LOW);
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if (i == split.length - 1) status.append(result);
                        else status.append(result).append(",");
                    }else {
                        if (i == split.length - 1) status.append("1");
                        else status.append("1,");
                    }
                    break;
                case "4":
                    if (output4 != null) output4.setSelection(split[i].equals("0") ? 0 : 1);
                    else CommandExec.execCommand(split[i].equals("1") ? Constant.OPEN_GP17 : Constant.CLOSE_GP17, false);

                    Log.d("test","i:" + i + "   length:" + ioId.length());
                    if (i == split.length - 1) status.append("0");
                    else status.append("0,");
                    break;
            }
            i+=1;
        }
        String message;
        if (status.toString().contains("1")) message = error;
        else message = "success";

        if (WaterQualityApplication.mtIsOk){
            MQTTService.publish(Constant.IoPublishTopic,new NodeBean(Constant.defDeviceId, status.toString(),ioId,setValue,message));
        }
        if (WaterQualityApplication.mt2IsOk){
            MQTTService2.publish(Constant.IoPublishTopic,new NodeBean(Constant.defDeviceId, status.toString(),ioId,setValue,message));
        }
    }
}
